package com.example.system.service.impl;

import cn.hutool.json.JSONUtil;
import com.example.common.enums.CodeEnum;
import com.example.common.exception.ServiceException;
import com.example.common.utils.ListPageUtil;
import com.example.common.utils.R;
import com.example.common.utils.StringUtils;
import com.example.system.dto.JobDTO;
import com.example.system.entity.QuartzJob;
import com.example.system.service.QuartzJobService;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;


import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author 王鹤蒲
 * @Date 2023 11 20
 */

@Service
public class QuartzJobServiceImpl implements QuartzJobService {

    @Resource
    private Scheduler scheduler;

    @Override
    public R addJob(QuartzJob quartzJob) throws SchedulerException, ClassNotFoundException {
        if (Objects.isNull(quartzJob)) {
            throw new ServiceException(CodeEnum.PARAMS_NOT_NULL.getCode(), "任务信息不能为空");
        }
        JobKey jobKey = JobKey.jobKey(quartzJob.getJobName(), quartzJob.getJobGroupName());
        if (!scheduler.checkExists(jobKey)) {
            Class<Job> jobClass = (Class<Job>) Class.forName(quartzJob.getJobClassName());
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).withIdentity(quartzJob.getJobName(), quartzJob.getJobGroupName()).withDescription(quartzJob.getJobName()).build();
            jobDetail.getJobDataMap().put("config", quartzJob.getConfig());
            TriggerKey triggerKey = TriggerKey.triggerKey(quartzJob.getTriggerName(), quartzJob.getTriggerGroupName());
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()).withMisfireHandlingInstructionDoNothing()).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(), CodeEnum.TIMING_TASK_ERROR.getMsg());
        }
        return R.success();
    }

    @Override
    public R deleteJob(String jobName, String jobGroupName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        if (scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
            return R.success();
        }
        return R.error(CodeEnum.TIMING_TASK_ERROR.getCode(), "任务不存在");
    }

    @Override
    public R pauseJob(String jobName, String jobGroupName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        if (scheduler.checkExists(jobKey)) {
            scheduler.pauseJob(jobKey);
            return R.success();
        }
        return R.error(CodeEnum.TIMING_TASK_ERROR.getCode(), "任务不存在");
    }

    @Override
    public R continueJob(String jobName, String jobGroupName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        if (scheduler.checkExists(jobKey)) {
            scheduler.resumeJob(jobKey);
            return R.success();
        }
        return R.error(CodeEnum.TIMING_TASK_ERROR.getCode(), "任务不存在");
    }

    @Override
    public QuartzJob getQuartzJob(String jobName, String jobGroupName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        if (!scheduler.checkExists(jobKey)) {
            return null;
        }
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (Objects.isNull(triggers)) {
            throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(), "未获取到触发器信息");
        }
        TriggerKey triggerKey = triggers.get(0).getKey();
        Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        QuartzJob job = new QuartzJob();
        job.setJobName(jobName).setJobGroupName(jobGroupName).setJobClassName(jobDetail.getJobClass().getName()).setDescription(jobDetail.getDescription()).setTriggerName(triggerKey.getName()).setTriggerGroupName(triggerKey.getGroup());
        if (Objects.nonNull(jobDetail.getJobDataMap())) {
            job.setConfig(JSONUtil.toJsonStr(jobDetail.getJobDataMap()));
        }
        if("NORMAL".equals(triggerState.toString())){
          job.setStatus(true);
        }else if("PAUSED".equals(triggerState.toString())){
          job.setStatus(false);
        }
        CronTrigger trigger = (CronTrigger) triggers.get(0);
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        job.setCronExpression(trigger.getCronExpression()).setPrevTime(simpleDateFormat.format(trigger.getPreviousFireTime())).setNextTime(simpleDateFormat.format(trigger.getNextFireTime())).setTimeZoneId(trigger.getTimeZone().getID());
        return job;
    }

    @Override
    public R getQuartzJobList(JobDTO jobDTO, int currentPage, int pageSize) throws SchedulerException {
        List<QuartzJob> quartzJobList=new ArrayList<>();
        List<String> triggerGroupNames=scheduler.getTriggerGroupNames();
        for(String triggerGroupName:triggerGroupNames){
            Set<TriggerKey> triggerKeySet=scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroupName));
            for(TriggerKey triggerKey:triggerKeySet){
              Trigger trigger=scheduler.getTrigger(triggerKey);
              JobKey jobKey=trigger.getJobKey();
              QuartzJob quartzJob=getQuartzJob(jobKey.getName(),jobKey.getGroup());
                System.out.println(quartzJob.getStatus());
              quartzJobList.add(quartzJob);
            }
        }
        if(quartzJobList.isEmpty()){
            return R.success(ListPageUtil.startPage(null,currentPage,pageSize));
        }
        quartzJobList=quartzJobList.stream().filter(quartzJob ->
              StringUtils.isNotEmpty(jobDTO.getJobName()) ? quartzJob.getJobName().equals(jobDTO.getJobName()) : true
          ).filter(quartzJob ->
                  StringUtils.isNotEmpty(jobDTO.getJobGroupName()) ? quartzJob.getJobGroupName().equals(jobDTO.getJobGroupName()) : true
          ).filter(quartzJob ->
                  Objects.nonNull(jobDTO.getStatus()) ? quartzJob.getStatus() == jobDTO.getStatus(): true
          ).collect(Collectors.toList());
          return R.success(ListPageUtil.startPage(quartzJobList,currentPage,pageSize));
    }

    @Override
    public R deleteBatch(List<JobDTO> jobDTOList) {
        try {
            for(JobDTO jobDTO:jobDTOList){
               deleteJob(jobDTO.getJobName(),jobDTO.getJobGroupName());
            }
        } catch (SchedulerException e) {
            throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(), CodeEnum.TIMING_TASK_ERROR.getMsg());
        }
        return R.success();
    }

    @Override
    public R updateJob(QuartzJob quartzJob,String quartzJobName,String quartzJobGroupName)  {
        try {
            if(StringUtils.isNotEmpty(quartzJob.getJobName())&&StringUtils.isNotEmpty(quartzJob.getJobGroupName())) {
                pauseJob(quartzJobName,quartzJobGroupName);
                deleteJob(quartzJobName,quartzJobGroupName);
                addJob(quartzJob);
            }else{
                throw new ServiceException(CodeEnum.PARAMS_NOT_NULL.getCode(), CodeEnum.PARAMS_NOT_NULL.getMsg());
            }
        } catch (SchedulerException e) {
            throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(),CodeEnum.TIMING_TASK_ERROR.getMsg());
        } catch (ClassNotFoundException e) {
            throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(),CodeEnum.TIMING_TASK_ERROR.getMsg());
        }
        return R.success();
    }

    @Override
    public R updateJobStatus(String jobName, String jobGroupName, Boolean status) {
        try {
            if(status){
                continueJob(jobName,jobGroupName);
            }else{
                pauseJob(jobName,jobGroupName);
            }
        } catch (SchedulerException e) {
            throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(),CodeEnum.TIMING_TASK_ERROR.getMsg());
        }
        return R.success();
    }
}
